/**
 * dxIntercom - JavaScript wrapper for the native WebRTC Streamer module.
 *
 * This module provides a simple interface to initialize WebRTC, make calls,
 * manage sessions, and handle intercom events on top of the native streamer.
 *
 * Usage:
 * - Call `dxIntercom.init(config)` once with a configuration object.
 * - Call `dxIntercom.setCallbacks({...})` to register event callbacks:
 *   - `onServiceStatus(status)`  // 'CONNECTED' | 'DISCONNECTED'
 *   - `onIncoming(sessionId)`
 *   - `onCallFail(sessionId, reason)`
 *   - `onCallStart(sessionId)`
 *   - `onCallEnd(sessionId)`
 * - Periodically call `dxIntercom.loop()` (e.g. in a timer) to poll native
 *   events and dispatch the callbacks above.
 * - Use `dxIntercom.call(serno, sessionId)` on the caller side to initiate a call.
 * - Use `dxIntercom.answer(sessionId)` / `dxIntercom.hangup(sessionId)` to answer
 *   or hang up a call.
 * - Use `dxIntercom.getServiceStatus()` to query current service status.
 * - Use `dxIntercom.setMute(muteMic, muteSpeaker)` to control local mic/speaker mute.
 * - Use `dxIntercom.getQuality(sessionId)` to query call quality stats.
 * - Call `dxIntercom.deinit()` when you no longer need the module.
 */
import * as native from './libvbar-m-dxintercom.so';

const dxIntercom = {};
let _callbacks = {};

// Internal state: only track one active session and current service status
let _currentSessionId = null;
let _serviceStatus = 'DISCONNECTED';

/**
 * Initialize WebRTC Streamer module.
 * @param {object} config - Configuration object.
 * @param {object} [config.rgb] - RGB Capturer configuration.
 * @param {object} [config.ivcore] - IVCore configuration.
 * @param {object} [config.venc] - Video Encoder configuration.
 * @param {object} [config.webrtc] - WebRTC configuration.
 * @param {string} [config.webrtc.initstring] - Initialization string.
 * @param {string} [config.webrtc.configuration] - Configuration string.
 * @param {string} [config.webrtc.serno] - Serial number.
 * @param {string} [config.webrtc.servers] - Server addresses.
 * @param {string} [config.webrtc.customerserno] - Customer serial number.
 * @throws {Error} If initialization fails.
 */
dxIntercom.init = function (config = {}) {
    native.init(config);
    _serviceStatus = 'DISCONNECTED';
    _currentSessionId = null;
};

/**
 * Deinitialize WebRTC Streamer module.
 * @throws {Error} If deinitialization fails.
 */
dxIntercom.deinit = function () {
    native.deinit();
    _serviceStatus = 'DISCONNECTED';
    _currentSessionId = null;
};


/**
 * Set callback handlers for intercom events.
 *
 * Available callbacks:
 * - onServiceStatus(status)
 *      - status: 'CONNECTED' | 'DISCONNECTED'
 * - onIncoming(sessionId, action)
 *      - Called on incoming call (callee side).
 *      - action: 'start' when an incoming call starts (rings),
 *                'end' when the incoming call ends before being answered (e.g. timeout).
 * - onCallFail(sessionId, reason)
 *      - Called when an outgoing call (caller side) fails.
 *      - reason: 'network' | 'rejected' | 'remote_busy' | 'no_answer' | 'internal'
 * - onCallStart(sessionId)
 *      - Called when media connection is successfully established (both sides).
 * - onCallEnd(sessionId)
 *      - Called when a call ends or the session is destroyed (both sides).
 *
 * @param {object} callbacks - Object containing callback functions listed above.
 */
dxIntercom.setCallbacks = function (callbacks) {
    _callbacks = callbacks || {};
};
/**
 * Main polling loop. Call this periodically (e.g. in a timer)
 * to fetch native events and dispatch high-level callbacks defined
 * via {@link dxIntercom.setCallbacks}.
 */
dxIntercom.loop = function () {
    const event = native.getEvent();
    if (!event) {
        return;
    }

    // Business-level event dispatch
    const type = event.type;
    const data = event.data || {};

    // type=1: incoming call or session information
    if (type === 1 && data.sessionId) {
        _currentSessionId = data.sessionId;
        if (typeof _callbacks.onIncoming === 'function') {
            const action = data.action || 'start';
            _callbacks.onIncoming(data.sessionId, action);
        }
    }

    // type=2: low-level WebRTC events
    if (type === 2 && typeof data.event === 'number') {
        const ev = data.event;

        // ONLINE / OFFLINE events: update service status and notify
        if (ev === 9) { // WEBRTC_EVENT_ONLINE
            _serviceStatus = 'CONNECTED';
            if (typeof _callbacks.onServiceStatus === 'function') {
                _callbacks.onServiceStatus('CONNECTED');
            }
        } else if (ev === 10 || ev === 14) { // OFFLINE / CONNECT_FAILD
            _serviceStatus = 'DISCONNECTED';
            if (typeof _callbacks.onServiceStatus === 'function') {
                _callbacks.onServiceStatus('DISCONNECTED');
            }
        }

        // CALL_LINK: media connection established
        if (ev === 6 && _currentSessionId && typeof _callbacks.onCallStart === 'function') {
            _callbacks.onCallStart(_currentSessionId);
        }

        // CALL_DISCONNECT / CALL_DESTORY: call ended
        if ((ev === 7 || ev === 8) && _currentSessionId) {
            if (typeof _callbacks.onCallEnd === 'function') {
                _callbacks.onCallEnd(_currentSessionId);
            }
            _currentSessionId = null;
        }
    }

    // type=5: outgoing call failure event
    if (type === 5 && data.sessionId) {
        const reason = data.reason || 'internal';
        if (typeof _callbacks.onCallFail === 'function') {
            _callbacks.onCallFail(data.sessionId, reason);
        }
        // If the failed call is the current session, clear local state
        if (_currentSessionId === data.sessionId) {
            _currentSessionId = null;
        }
    }
};


/**
 * Close a WebRTC session.
 * @param {string} sessionId - Session ID to close.
 * @throws {Error} If closing session fails.
 */
dxIntercom.closeSession = function (sessionId) {
    if (sessionId === undefined || sessionId === null) {
        throw new Error("dxIntercom.closeSession: 'sessionId' parameter is required");
    }
    if (typeof sessionId !== 'string') {
        throw new Error("dxIntercom.closeSession: 'sessionId' must be a string");
    }
    native.closeSession(sessionId);
};

/**
 * Get current service connection status.
 * This is a cached value synchronized from native events:
 * it always returns either 'DISCONNECTED' or 'CONNECTED'.
 *
 * @returns {'DISCONNECTED' | 'CONNECTED'} Current service status.
 */
dxIntercom.getServiceStatus = function () {
    try {
        const status = native.getServiceStatus();
        if (status === 'CONNECTED' || status === 'DISCONNECTED') {
            _serviceStatus = status;
        }
    } catch (e) {
        // Ignore underlying exceptions and maintain the existing state.
    }
    return _serviceStatus;
};

/**
 * Set mute state for local microphone and local speaker.
 *
 * Internally this is implemented by controlling whether audio
 * frames are pushed to / pulled from WebRTC, without changing
 * hardware volumes.
 *
 * @param {boolean} muteMic - If true, stop sending local audio to remote.
 * @param {boolean} muteSpeaker - If true, stop playing remote audio locally.
 */
dxIntercom.setMute = function (muteMic, muteSpeaker) {

    native.setMute(!!muteMic, !!muteSpeaker);
};

/**
 * Query call quality statistics for a given session.
 *
 * @param {string} sessionId - Target session ID.
 * @returns {object} Quality metrics from native layer.
 * @throws {Error} If sessionId is missing or invalid.
 */
dxIntercom.getQuality = function (sessionId) {
    if (!sessionId) {
        throw new Error("dxIntercom.getQuality: 'sessionId' parameter is required");
    }
    return native.getQuality(sessionId);
};

/**
 * Initiate a WebRTC call (Caller).
 * @param {string} serno - Target device serial number
 * @param {string} sessionId - Session ID (Generated by the business layer, used for call association confirmation.).
 * @throws {Error} If call initiation fails.
 */
dxIntercom.call = function (serno, sessionId) {
    if (serno === undefined || serno === null) {
        throw new Error("dxIntercom.call: 'serno' parameter is required");
    }
    if (typeof serno !== 'string') {
        throw new Error("dxIntercom.call: 'serno' must be a string");
    }
    if (sessionId === undefined || sessionId === null) {
        throw new Error("dxIntercom.call: 'sessionId' parameter is required");
    }
    if (typeof sessionId !== 'string') {
        throw new Error("dxIntercom.call: 'sessionId' must be a string");
    }
    native.call(serno, sessionId);
    _currentSessionId = sessionId;
};

/**
 * Answer an incoming call 
 * @param {string} sessionId - Session ID of the incoming call.
 * @throws {Error} If answer fails.
 */
dxIntercom.answer = function (sessionId) {
    if (sessionId === undefined || sessionId === null) {
        throw new Error("dxIntercom.answer: 'sessionId' parameter is required");
    }
    if (typeof sessionId !== 'string') {
        throw new Error("dxIntercom.answer: 'sessionId' must be a string");
    }
    native.answer(sessionId);
};

/**
 * Hangup a call 
 * @param {string} sessionId - Session ID of the call to hangup.
 * @throws {Error} If hangup fails.
 */
dxIntercom.hangup = function (sessionId) {
    if (sessionId === undefined || sessionId === null) {
        throw new Error("dxIntercom.hangup: 'sessionId' parameter is required");
    }
    if (typeof sessionId !== 'string') {
        throw new Error("dxIntercom.hangup: 'sessionId' must be a string");
    }
    native.hangup(sessionId);
    if (_currentSessionId === sessionId) {
        _currentSessionId = null;
    }
};

export default dxIntercom;
